using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.Xml;
using System.Xml.Serialization;
using System.Net;
using Ltf.Remoting.TeamFoundation;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
//comment go here
namespace Ltf.Remoting.TFAccess
{
	[Serializable]
	public class LizardChangeset
	{
		public int ChangesetId;
		public DateTime CreationDate;
		public string Commiter;
		public string Comment;
		public string CheckinNote;
		public string WorkItemIds;
		public bool IsFromBranch;
		public LizardChange[] Changes;
	}

	[Serializable]
	public struct LizardChange
	{
		public string ChangeType;
		public string ServerItem;
		public int ItemId;
		public int ChangesetId;
		public bool IsRename;
	}

	public enum BranchResolutionType
	{
		None,
		BranchesToFromRootPathOnly,
		RecursiveBranchToResolution
	}

	public class TFHistory
	{
		private class IncompleteBranchHistory
		{
			public string serverPath;
			public int serverPathVersion;
			public int serverPathDeletionId;

			public int procesedFromChangeset = 0;
			public int procesedToChangeset = 0;

			public int blockSize = 200;

			public bool IsFromBranch;

			public IncompleteBranchHistory(string serverPath, int serverPathVersion, int serverPathDeletionId)
			{
				this.serverPath = serverPath;
				this.serverPathVersion = serverPathVersion;
				this.serverPathDeletionId = serverPathDeletionId;
			}
		}

		private string path;
		private string serverPath;
		private int serverPathVersion;
		private int serverPathDeletionId;
		private Workspace workspace;
		private Dictionary<int, LizardChangeset> changes;
		private BranchResolutionType branchResolutionType;

		private Queue<IncompleteBranchHistory> incompleteBranchHistories;

		private TFServerConnection connection;

		public BranchResolutionType BranchResolutionType
		{
			get { return branchResolutionType; }
			set { branchResolutionType = value; }
		}

		public TFHistory(string path)
		{
			this.path = path;
			serverPathDeletionId = 0;
			connection = ServerConnections.GetConnectionForPath(path);
			workspace = connection.GetWorkspace(path);
			serverPath = workspace.GetServerItemForLocalItem(path);
			ItemSpec[] iss = new ItemSpec[] { new ItemSpec(path, RecursionType.None) };
			ExtendedItem[][] extItems = workspace.GetExtendedItems(iss, DeletedState.Any, ItemType.Any);
			changes = new Dictionary<int, LizardChangeset>();
			serverPathVersion = extItems[0][0].VersionLocal;
			serverPathDeletionId = extItems[0][0].DeletionId;
			incompleteBranchHistories = new Queue<IncompleteBranchHistory>();
			incompleteBranchHistories.Enqueue(new IncompleteBranchHistory(serverPath, serverPathVersion, serverPathDeletionId));
		}

		public TFHistory(TFServerConnection connection, string serverPath, Workspace workspace)
		{
			this.connection = connection;

			string name;
			LizardFileAccess.GetNameAndVersionFromPath(serverPath, out name, out serverPathVersion, out serverPathDeletionId);

			this.serverPath = name;
			this.path = workspace.GetLocalItemForServerItem(name);
			this.workspace = workspace;
			//			serverPath = workspace.GetServerItemForLocalItem(path);
			changes = new Dictionary<int, LizardChangeset>();
			incompleteBranchHistories = new Queue<IncompleteBranchHistory>();
			incompleteBranchHistories.Enqueue(new IncompleteBranchHistory(serverPath, serverPathVersion, serverPathDeletionId));
		}

		public void Reset()
		{
			incompleteBranchHistories.Clear();
		}


		public IEnumerable<LizardChangeset> Changesets
		{
			get { return changes.Values; }
		}

		public int Count
		{
			get { return changes.Count; }
		}

		public void GetHistory()
		{
			int setTotal = 0;
			GetHistory(serverPath, serverPathVersion, serverPathDeletionId, false, 0, 1, 200, RecursionType.Full, false, ref setTotal, null);
		}

		public void GetHistory(RecursionType recursionType)
		{
			int setTotal = 0;
			GetHistory(serverPath, serverPathVersion, serverPathDeletionId, false, 0, 1, 200, recursionType, false, ref setTotal, null);
		}

		public void GetHistory(int changesetId, RecursionType recursionType)
		{
			int setTotal = 0;
			GetHistory(serverPath, serverPathVersion, serverPathDeletionId, false, changesetId, 1, 200, recursionType, false, ref setTotal, null);
		}

		public void GetHistory(bool includeWorkItems, int from, int maxCount)
		{
			int setTotal = 0;
			GetHistory(serverPath, serverPathVersion, serverPathDeletionId, includeWorkItems, from, 1, maxCount, RecursionType.Full, false, ref setTotal, null);

		}
		public void GetHistory(bool includeWorkItems
			, int from, int maxCount, RecursionType recursionType)
		{
			int setTotal = 0;
			GetHistory(serverPath, serverPathVersion, serverPathDeletionId, includeWorkItems, from, 1,  maxCount, recursionType, false, ref setTotal, null);
		}

		public void GetHistory(bool includeWorkItems
	, int versionFrom, int versionTo, int maxCount, RecursionType recursionType)
		{
			int setTotal = 0;
			GetHistory(serverPath, serverPathVersion, serverPathDeletionId, includeWorkItems, versionFrom, versionTo, maxCount, recursionType, false, ref setTotal, null);
		}


		private int GetHistory(string path, bool includeWorkItems
	, int versionFrom, int versionTo, int maxCount, RecursionType recursionType, bool isFromBranch, ref int setTotal, List<string> fetchedHistoryPaths)
		{
			int version;
			int deletionId;
			string name;
			LizardFileAccess.GetNameAndVersionFromPath(path, out name, out version, out deletionId);
			IncompleteBranchHistory ibh = new IncompleteBranchHistory(name, version, deletionId);
			ibh.IsFromBranch = true;
			ibh.procesedFromChangeset = versionFrom;
			List<IncompleteBranchHistory> q = new List<IncompleteBranchHistory>(incompleteBranchHistories);
			q.Insert(0, ibh);
			incompleteBranchHistories = new Queue<IncompleteBranchHistory>(q);
			return GetHistory(name, version, deletionId, includeWorkItems, versionFrom, versionTo, maxCount, recursionType, true, ref setTotal, fetchedHistoryPaths);

		}

		private int GetHistory(string path, int version, int deletionId, bool includeWorkItems
			, int versionFrom, int versionTo, int setSize, RecursionType recursionType, bool isFromBranch, ref int setTotal, List<string> fetchedHistoryPaths)
		{
			object history;
			int maxCount = setSize;
			IncompleteBranchHistory ibh;
			if (incompleteBranchHistories.Count > 0)
			{
				ibh = incompleteBranchHistories.Dequeue();
			}
			else
			{
				ibh = new IncompleteBranchHistory(path, version, deletionId);
			}
			while ((setSize == -1 || setTotal < setSize) && ibh != null)
			{
				path = ibh.serverPath;
				version = ibh.serverPathVersion;
				deletionId = ibh.serverPathDeletionId;
				isFromBranch = ibh.IsFromBranch;

				VersionSpec pathVersionSpec = (version == 0) ? ChangesetVersionSpec.Latest : new ChangesetVersionSpec(version);

				// if we were already up to a certain point, carry on from that point plus 1
				if (ibh.procesedToChangeset != 0)
				{
					versionFrom = ibh.procesedToChangeset;
					maxCount = setSize + 1;
				}

				VersionSpec vsFrom = (versionFrom == 0) ? ChangesetVersionSpec.Latest : new ChangesetVersionSpec(versionFrom);
				VersionSpec vsTo = (versionTo == 0) ? ChangesetVersionSpec.Latest : new ChangesetVersionSpec(versionTo);

				if (maxCount == -1)
					maxCount = int.MaxValue;

				string pathName;
				int ver;

				LizardFileAccess.GetNameAndVersionFromPath(path, out pathName, out ver);


				VersionControlServer vcs = connection.VersionControlServer;
				history = vcs.QueryHistory(pathName, pathVersionSpec, serverPathDeletionId,
					recursionType, "", vsTo,
					vsFrom, maxCount, true, false);

				System.Collections.IEnumerable changesets = (System.Collections.IEnumerable)history;
				StringBuilder sb = new StringBuilder();
				if (fetchedHistoryPaths==null)
					fetchedHistoryPaths = new List<string>();
				List<int> recursedBranchVersions = new List<int>();
				try
				{
					bool isFinishedIncomplete = false;
					foreach (Changeset c in changesets)
					{
						LizardChangeset lcs = new LizardChangeset();
						string notes = string.Empty;
						List<LizardChange> details = new List<LizardChange>();
						List<Change> changeList = new List<Change>(c.Changes);
						changeList.Sort(ChangeSorter);
						lcs.ChangesetId = c.ChangesetId;
						lcs.CreationDate = c.CreationDate;
						lcs.Commiter = c.Committer;
						lcs.Comment = c.Comment;
						ibh.procesedToChangeset = lcs.ChangesetId;

						if (!changes.ContainsKey(lcs.ChangesetId))
							setTotal++;


						changes[lcs.ChangesetId] = lcs;

						List<string> branchedItems = new List<string>();
						foreach (Change ch in changeList)
						{
							LizardChange lc = new LizardChange();
							lc.ChangeType = ch.ChangeType.ToString();
							lc.ServerItem = ch.Item.ServerItem;
							details.Add(lc);
							if ((ch.ChangeType & ChangeType.Branch) == ChangeType.Branch)
							{
								if (branchResolutionType == BranchResolutionType.RecursiveBranchToResolution
									 || (branchResolutionType == BranchResolutionType.BranchesToFromRootPathOnly
												&& ch.Item.ServerItem == serverPath))
								{
									if (!recursedBranchVersions.Contains(ch.Item.ChangesetId))
									{
										string s = string.Empty;
										int v = GetBranchHistory(ch.Item.ServerItem, new ChangesetVersionSpec(ch.Item.ChangesetId), ref s, maxCount,
											includeWorkItems, recursionType, fetchedHistoryPaths, ref setTotal);
										recursedBranchVersions.Add(ch.Item.ChangesetId);
									}
									branchedItems.Add(ch.Item.ServerItem);

								}
							}
						}

						// any branches we should consider?
						if (branchedItems.Count > 0)
						{
							//get just unique paths (eg. drop a/b/c if we already have a/b)
							List<string> uniqueItems = GetUniquePaths(branchedItems);
							foreach (string branchedItem in uniqueItems)
							{
								// get the history for any branches
								string s = string.Empty;
								int v = GetBranchHistory(branchedItem, new ChangesetVersionSpec(lcs.ChangesetId), ref s, setSize,
									includeWorkItems, recursionType, fetchedHistoryPaths, ref setTotal);

							}
						}

						foreach (CheckinNoteFieldValue cnfv in c.CheckinNote.Values)
						{
							notes += ((notes == string.Empty) ? "" : ", ") + string.Format("{0} = {1}", cnfv.Name, cnfv.Value);
						}
						lcs.Changes = details.ToArray();
						lcs.CheckinNote = notes;
						lcs.IsFromBranch = isFromBranch;
						if (includeWorkItems)
						{
							List<string> workItemIds = new List<string>();
							foreach (WorkItem wi in c.WorkItems)
							{
								workItemIds.Add(wi.Id.ToString());
							}
							lcs.WorkItemIds = string.Join(",", workItemIds.ToArray());
						}

						if (setTotal >= setSize && setSize!= -1)
						{
							isFinishedIncomplete = true;
							break;
						}

					}
					if (isFinishedIncomplete)
					{
						incompleteBranchHistories.Enqueue(ibh);
					}
					else
					{
						if (incompleteBranchHistories.Count > 0)
							ibh = incompleteBranchHistories.Dequeue();
						else
							ibh = null;
					}
					IsMainHistoryFinished = !isFinishedIncomplete && !isFromBranch;
				}
				catch (Exception ex)
				{
					string m = ex.Message;
					ibh = null;
					// no history, probably a new item.
				}
			}
			return setTotal;
		}

		private bool isMainHistoryFinished;

		public bool IsMainHistoryFinished
		{
			get { return isMainHistoryFinished; }
			set { isMainHistoryFinished = value; }
		}


		public int ChangeSorter(Change x, Change y)
		{
			return x.Item.ServerItem.CompareTo(y.Item.ServerItem);
		}

		public Changeset GetChangesetDetails(int changesetId)
		{
			VersionControlServer vcs = connection.VersionControlServer;
			Changeset c = vcs.GetChangeset(changesetId);
			return c;
		}

		public int GetPreviousRevisionId(VersionSpec versionSpec)
		{
			object history;
			int v;
			int deletionId;
			string name;
			LizardFileAccess.GetNameAndVersionFromPath(path, out name, out v, out deletionId);
			VersionControlServer vcs = connection.VersionControlServer;
			history = vcs.QueryHistory(name, versionSpec, deletionId,
				RecursionType.Full, "", new ChangesetVersionSpec(1),
				versionSpec, 2, false, false);
			int previous = 0;
			System.Collections.IEnumerable changesets = (System.Collections.IEnumerable)history;
			foreach (Changeset c in changesets)
			{
				previous = c.ChangesetId;
			}
			return previous;
		}

		/// <summary>
		/// Gets the server latest.
		/// </summary>
		/// <param name="versionSpec">The version spec.</param>
		/// <returns></returns>
		public int GetServerLatest(VersionSpec versionSpec)
		{
			object history;
			VersionControlServer vcs = connection.VersionControlServer;
			//VersionSpec pathSpec = VersionSpec.Latest;
			//if (changesetId!=0)
			//  pathSpec = new ChangesetVersionSpec(changesetId);
			//Item item = vcs.GetItem(path, pathSpec);
			history = vcs.QueryHistory(serverPath, versionSpec, serverPathDeletionId,
				RecursionType.Full, "", new ChangesetVersionSpec(1),
				LatestVersionSpec.Latest, 2, true, false);
			int latest = 0;
			System.Collections.IEnumerable changesets = (System.Collections.IEnumerable)history;
			foreach (Changeset c in changesets)
			{
				bool skip = false;
				foreach (Change ch in c.Changes)
				{
					if (ch.Item.ServerItem == serverPath && ((ch.ChangeType & ChangeType.Delete) == ChangeType.Delete))
					{
						skip = true;
						break;
					}

				}
				if (!skip)
				{
					latest = c.ChangesetId;
					break;
				}
			}
			return latest;
		}

		public int GetBranchHistory(string branchedItem, ChangesetVersionSpec versionSpec, ref string fromPath, int maxCount,
			bool includeWorkItems, RecursionType recursionType,
			List<string> fetchedHistoryPaths, ref int setTotal)
		{
			//			branchParentpath = path;
			VersionControlServer vcs = connection.VersionControlServer;
			BranchHistoryTreeItem[] bhti = new BranchHistoryTreeItem[0]; ;
			//try
			//{
			//  bhti = vcs.GetBranchHistory(new ItemSpec[] { new ItemSpec(path, RecursionType.Full, 0) }, versionSpec)[0];
			//}
			//catch
			//{
			bhti = vcs.GetBranchHistory(new ItemSpec[] { new ItemSpec(branchedItem, RecursionType.None, 0) }, versionSpec)[0];
			//}


			fromPath = string.Empty;
			int postBranchChangeSet = 0;
			string matchPath = branchedItem;
			if (!branchedItem.StartsWith("$"))
				matchPath = vcs.GetWorkspace(branchedItem).GetServerItemForLocalItem(branchedItem);
			foreach (BranchHistoryTreeItem ti in bhti)
			{
				postBranchChangeSet = GetMaxBranchChangeSet(matchPath, ti, postBranchChangeSet, ref fromPath);
			}
			if (postBranchChangeSet != 0)
			{
				string fromPathName;
				int fromPathV;
				LizardFileAccess.GetNameAndVersionFromPath(fromPath, out fromPathName, out fromPathV);


				if (!fetchedHistoryPaths.Contains(fromPath) &&
					!fetchedHistoryPaths.Contains(Path.GetDirectoryName(fromPath).Replace('\\', '/')))
				{
					fetchedHistoryPaths.Add(fromPath);
					GetHistory(fromPath, includeWorkItems, postBranchChangeSet - 1, 1, maxCount, recursionType, true, ref setTotal, fetchedHistoryPaths);
				}
			}

			return postBranchChangeSet;
		}

		private int GetMaxBranchChangeSet(string matchPath, BranchHistoryTreeItem treeItem, int currentMax, ref string fromPath)
		{
			string fname = string.Empty;
			Item relToItem = treeItem.Relative.BranchToItem;
			Item relFromItem = treeItem.Relative.BranchFromItem;
			if (relFromItem != null)
				fname = relFromItem.ServerItem;
			if (relFromItem != null && relToItem.ServerItem.StartsWith(matchPath) && relToItem.ChangesetId >= currentMax)
			{
				currentMax = relToItem.ChangesetId;
				string name = fromPath;
				int cp = fromPath.IndexOf(';');
				if (cp > 0)
					name = name.Substring(0, cp);
				if (relFromItem.ServerItem.Length < name.Length || string.IsNullOrEmpty(fromPath))
					fromPath = LizardFileAccess.SetPathVersionAndDeletionId(relFromItem.ServerItem, relFromItem.ChangesetId, relFromItem.DeletionId);
			}

			foreach (BranchHistoryTreeItem child in treeItem.Children)

				currentMax = GetMaxBranchChangeSet(matchPath, child, currentMax, ref fromPath);

			return currentMax;
		}

		public List<string> GetUniquePaths(List<string> paths)
		{
			List<string> filteredPaths = new List<string>();
			if (paths.Count == 0)
				return filteredPaths;
			paths.Sort();

			string testPath = paths[0];
			filteredPaths.Add(testPath);

			for (int i = 1; i < paths.Count; i++)
			{
				if (!paths[i].StartsWith(testPath + "/"))
				{
					testPath = paths[i];
					filteredPaths.Add(testPath);
				}
			}


			return filteredPaths;
		}
	}
}
